package cn.tedu.csmall.sso.filter;


import cn.tedu.csmall.commons.restful.JsonResult;
import cn.tedu.csmall.commons.restful.ResponseCode;
import cn.tedu.csmall.commons.utils.JwtUtils;
import cn.tedu.csmall.sso.utils.LoginPrincipal;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

/**
 * 处理JWT的过滤器
 * 1.尝试获取请求中的数据
 * 2.没有获取到有效jwt，直接放行
 * 3.如果获取到有效的JWT，尝试解析
 * 4.解析后得到的结果会存入到Spring Security的请求上下文中
 */
@Component
@Slf4j
public class JwtAuthorizationFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //JWT默认的存储请求头的值为Authorization
        String jwt = request.getHeader("Authorization");
        log.info("从请求头中获取JWT的数据：{}",jwt);

        //先判断是否获取到有效的JWT数据，无JWT数据，直接放行
        if (!StringUtils.hasText(jwt)){
            log.info("请求头中的JWT数据是无效的，直接放行");
            filterChain.doFilter(request,response);
            return;
        }

        //如果获取到有效的JWT值，尝试进行解析
        Claims claims =null;
        try{
            claims= JwtUtils.parse(jwt);

        }catch (ExpiredJwtException e){
            log.info("解析JWT失败，JWT数据有误：{}，{}",e.getClass().getName(),e.getMessage());
            String message="获取登陆信息失败，请重新登陆";
            JsonResult jsonResult=JsonResult.failed(ResponseCode.ERR_JWT_EXPIRED,message);
            String jsonString = JSON.toJSONString(jsonResult);
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().println(jsonString);
            return;
        }catch (SignatureException e){
            log.info("解析JWT失败，JWT数据有误：{}，{}",e.getClass().getName(),e.getMessage());
            String message="获取登陆信息失败，请重新登陆";
            JsonResult jsonResult=JsonResult.failed(ResponseCode.INTERNAL_SERVER_ERROR,message);
            String jsonString = JSON.toJSONString(jsonResult);
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().println(jsonString);
            return;
        }catch (MalformedJwtException e){
            log.info("解析JWT失败，JWT数据有误：{}，{}",e.getClass().getName(),e.getMessage());
            String message="获取登陆信息失败，请重新登陆";
            JsonResult jsonResult=JsonResult.failed(ResponseCode.INTERNAL_SERVER_ERROR,message);
            String jsonString = JSON.toJSONString(jsonResult);
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().println(jsonString);
            return;
        }catch (Throwable e){
            log.info("解析JWT失败，JWT数据有误：{}，{}",e.getClass().getName(),e.getMessage());
            String message="获取登陆信息失败，请重新登陆";
            JsonResult jsonResult=JsonResult.failed(ResponseCode.INTERNAL_SERVER_ERROR,message);
            String jsonString = JSON.toJSONString(jsonResult);
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().println(jsonString);
            return;
        }
        Object id = claims.get("id");
        log.info("从JWT中解析得到的用户id：{}",id);
        Object username = claims.get("username");
        log.info("从JWT中解析得到的用户名：{}",username);
        Object authorities = claims.get("authorities");
        log.info("从JWT中解析得到的用户权限列表：{}",authorities);
        List<SimpleGrantedAuthority> authorityList = JSON.parseArray(authorities.toString(), SimpleGrantedAuthority.class);
        LoginPrincipal loginPrincipal = new LoginPrincipal();
        loginPrincipal.setId(Long.parseLong(id.toString()));
        loginPrincipal.setUsername(username.toString());

        //当解析成功后，应该将相关数据存储到Spring Security的上下文中
        SecurityContext context = SecurityContextHolder.getContext();
        //登陆成功后，密码无需传入，给值为null
        Authentication authentication=new UsernamePasswordAuthenticationToken(
                loginPrincipal,null,authorityList
        );
        context.setAuthentication(authentication);

        //请求放行
        filterChain.doFilter(request,response);

    }
}
